package ferp.core.ai.filter;

import ferp.core.ai.tree2.Evaluator;
import ferp.core.card.Card;
import ferp.core.game.Game;
import ferp.core.game.Trick;
import ferp.core.player.Hand;

/**
 * User: olegs
 * Date: 9/20/13 9:48 PM
 */
public class FilterWhisterAfterDeclarerMoveOpen
{
  public static int apply(Evaluator evaluator, int candidates, int[] hands, int player, int trick, Card.Suit trump, int round, int hand, int declarer)
  {
    int filtered = 0;
    Card lowest1 = getLowestBeatCard(evaluator, trick, candidates, player, hand, trump);
    int lowestCandidates1 = getLowestCandidates(candidates);

    // could be either 1 or 2
    switch (hand)
    {
      case 1:
        // no beat card found
        if (lowest1 == null)
          // highest card in trick belongs to declarer - return lowest rank cards in each suit only
          filtered = Hand.add(filtered, lowestCandidates1);
        else
        {
          // find a minimal next player's card that beats declarer's one considering player's card in trick
          int next = Game.next(player);
          int candidates2 = evaluator.getMoveCandidates(hands, next, trick, trump, round, hand + 1, declarer);
          Card lowest2 = getLowestBeatCard(evaluator, trick, candidates, player, candidates2, next, hand, trump);

          // in case partner can't beat declarer card with lowest one than player use lowest1 as single candidate
          // in case lowest2 is lower than lowest1 and they have the same suit use the lowest candidates only
          if (lowest2 == null || (lowest2.suit == lowest1.suit && lowest2.rank.ordinal() > lowest1.rank.ordinal()))
            filtered = Hand.add(filtered, lowest1);
          else
            filtered = Hand.add(filtered, lowestCandidates1);
        }

        break;

      case 2:
        byte winner = Trick.winner(trick, trump);

        // if current trick's winner is declarer try to win the trick by playing with the lowest card that can beat declarer's one
        // if current trick's winner is a partner try to not over beat his card
        if (winner == declarer)
          filtered = lowest1 == null ? Hand.add(filtered, lowestCandidates1) : Hand.add(filtered, lowest1);
        else
          filtered = Hand.add(filtered, lowestCandidates1);

        break;
    }

    return filtered == 0 ? candidates : filtered;
  }

  static int getLowestCandidates(int candidates)
  {
    int filtered = 0;

    for (Card.Suit s : Card.Suit.rising)
    {
      Card.Rank lowest = Hand.lowest(candidates, s);

      if (lowest != null)
        filtered = Hand.add(filtered, s, lowest);
    }

    return filtered;
  }

  static Card getLowestBeatCard(Evaluator evaluator, int trick, int candidates, int player, int hand, Card.Suit trump)
  {
    Card lowest = null;

    for (int h = evaluator.start(candidates); h != 0; h = evaluator.next(candidates, h))
    {
      Card c = Card.card(h);

      trick = Trick.add(trick, player, c.compressed);

      if (Trick.winner(trick, trump) == player && (lowest == null || c.rank.ordinal() < lowest.rank.ordinal()))
        lowest = c;

      trick = Trick.remove(trick, hand);
    }

    return lowest;
  }

  private static Card getLowestBeatCard(Evaluator evaluator, int trick, int candidates, int player, int candidates2, int next, int hand, Card.Suit trump)
  {
    Card lowest = null;

    for (int h = evaluator.start(candidates); h != 0; h = evaluator.next(candidates, h))
    {
      Card c1 = Card.card(h);

      trick = Trick.add(trick, player, c1.compressed);

      Card c2 = getLowestBeatCard(evaluator, trick, candidates2, next, hand + 1, trump);

      if (c2 != null && (lowest == null || c2.rank.ordinal() < lowest.rank.ordinal()))
        lowest = c2;

      trick = Trick.remove(trick, hand);
    }

    return lowest;
  }
}
